package com.zly.demo.rediscache.aop;

import com.zly.demo.rediscache.annotation.RedisCache;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class RedisCacheAop {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Around("@annotation(com.zly.demo.rediscache.annotation.RedisCache)")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            RedisCache annotation = method.getAnnotation(RedisCache.class);
            if (annotation != null) {
                String prefix = annotation.prefix();
                String matchPattern = annotation.matchPattern();
                SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
                Expression expression = spelExpressionParser.parseExpression(matchPattern);
                StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext();
                Object[] args = joinPoint.getArgs();
                DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
                String[] parameterNames = discoverer.getParameterNames(method);
                if (parameterNames != null) {
                    for (int i = 0; i < parameterNames.length; i++) {
                        standardEvaluationContext.setVariable(parameterNames[i], args[i]);
                    }
                }
                String redisKey = prefix + expression.getValue(standardEvaluationContext);
                result = redisTemplate.opsForValue().get(redisKey);
                if (result != null) {
                    return result;
                } else {
                    result = joinPoint.proceed();
                    if (result != null) {
                        redisTemplate.opsForValue().set(redisKey, result);
                    }
                    return result;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return null;
    }
}
